#ifndef LMN_HYPERLINK_H
#define LMN_HYPERLINK_H

#include "lmntal.h"
#include "atom.h"
#include "vector.h"
#include "internal_hash.h"
#include "membrane.h"

typedef int HyperLink; // hyperlink id

typedef struct HyperLinkName {
  int rank;          // 子孫数(自身以外)
  int element;       // 要素数(自身含む)
  LmnFunctor func;
//  LmnFunctor coname; // conameを持たない場合はfuncと等しい
  HyperLink  parent; // rootの場合は自身のid, conameを持つ場合はconameのid
  struct st_table   *children; // 子(name)を管理する表

} HyperLinkName;

typedef struct HyperLinkConame {
  int rank;
  int element;
  LmnFunctor func;
  HyperLink  parent; // rootの場合は自身のid
  struct st_table *children; // 子(coname)を管理する表

} HyperLinkConame;

typedef struct HyperLinkSystem {
  HyperLink hyperlink_id;
  int       ary_size;

  struct st_table  *func_to_name;
  void **name;                  // name管理配列
  struct st_table *coname;     // coname管理表
  struct st_table *fc_link_hl; // アトムとhyperlinkアトムとの接続関係を保持
} HyperLinkSystem;

extern struct HyperLinkSystem hl;

#define LMN_HLINK_CURRENT_ID hl.hyperlink_id

#define LMN_NAME(H) (((HyperLinkName **)hl.name)[(H)])
#define LMN_NAME_RANK(H) (((HyperLinkName **)hl.name)[(H)]->rank)
#define LMN_NAME_ELEM(H) (((HyperLinkName **)hl.name)[(H)]->element)
#define LMN_NAME_FUNC(H) (((HyperLinkName **)hl.name)[(H)]->func)
#define LMN_NAME_CONAME(H) (((HyperLinkName **)hl.name)[(H)]->coname)
#define LMN_NAME_PARENT(H) (((HyperLinkName **)hl.name)[(H)]->parent)
#define LMN_NAME_CHILD_TBL(H) (((HyperLinkName **)hl.name)[(H)]->children)

#define LMN_CONAME(H) (hyperlink_get_coname(H))
#define LMN_CONAME_RANK(H) ((hyperlink_get_coname(H))->rank)
#define LMN_CONAME_ELEM(H) ((hyperlink_get_coname(H))->element)
#define LMN_CONAME_FUNC(H) ((hyperlink_get_coname(H))->func)
#define LMN_CONAME_PARENT(H) ((hyperlink_get_coname(H))->parent)
#define LMN_CONAME_CHILD_TBL(H) ((hyperlink_get_coname(H))->children)


#define LMN_ATOM_GET_HLINK_ID(a)  (lmn_hyperlink_fc_to_hl(LMN_SATOM_GET_FUNCTOR(LMN_SATOM(a))))
#define LMN_SATOM_GET_HLINK_ID(a) (lmn_hyperlink_fc_to_hl(LMN_SATOM_GET_FUNCTOR(a)))
#define LMN_HLINK_REVERSE(f) (lmn_hyperlink_hl_to_fc(lmn_hyperlink_fc_to_hl(f) * -1))
#define LMN_HLINK_STR(H) (lmn_id_to_name(LMN_FUNCTOR_NAME_ID(lmn_hyperlink_hl_to_fc(H))))

LMN_EXTERN void lmn_hyperlink_init();
LMN_EXTERN void lmn_hyperlink_destroy();
LMN_EXTERN BOOL lmn_hyperlink_initialized();

LMN_EXTERN LmnFunctor lmn_hyperlink_new_func(void);
LMN_EXTERN LmnFunctor lmn_hyperlink_set_coname(HyperLink H);

/* LmnFunctor <-> HyperLink */
LMN_EXTERN HyperLink lmn_hyperlink_fc_to_hl(LmnFunctor f);
LMN_EXTERN LmnFunctor lmn_hyperlink_hl_to_fc(HyperLink h);
LMN_EXTERN LmnFunctor lmn_hyperlink_fc_to_rep_fc(LmnFunctor f);

LMN_EXTERN BOOL lmn_is_hyperlink_name(LmnFunctor f);
LMN_EXTERN BOOL lmn_is_hyperlink_coname(LmnFunctor f);
LMN_EXTERN BOOL lmn_is_hyperlink(LmnFunctor f);
LMN_EXTERN BOOL lmn_has_hyperlink_coname(LmnFunctor f);

LMN_EXTERN int lmn_hyperlink_rank_num(HyperLink h);
LMN_EXTERN int lmn_hyperlink_element_num(HyperLink h);
LMN_EXTERN void lmn_hyperlink_element_add(HyperLink h, int add);
LMN_EXTERN void lmn_hyperlink_element_sub(HyperLink h, int sub);

LMN_EXTERN void lmn_hyperlink_free_mem(LmnMembrane *mem);

LMN_EXTERN HyperLink lmn_hyperlink_get_root(HyperLink name_id);
LMN_EXTERN HyperLink lmn_hyperlink_get_representative(HyperLink h);
LMN_EXTERN void lmn_hyperlink_get_children(Vector *vec, HyperLink h);
LMN_EXTERN HyperLink lmn_hyperlink_unify_n_n(HyperLink root_id1, HyperLink root_id2);
LMN_EXTERN HyperLink lmn_hyperlink_unify_n_c(HyperLink name_id, HyperLink coname_id);
LMN_EXTERN HyperLink lmn_hyperlink_unify_c_c(HyperLink root_id1, HyperLink root_id2);
LMN_EXTERN HyperLink lmn_hyperlink_unify(HyperLink h1, HyperLink h2);
LMN_EXTERN BOOL lmn_hyperlink_cmp_func(LmnFunctor func1, LmnFunctor func2);
LMN_EXTERN BOOL lmn_hyperlink_cmp_atom(LmnAtom atom1, LmnLinkAttr attr1, LmnAtom atom2, LmnLinkAttr attr2);
//LMN_EXTERN void lmn_hyperlink_set_linked_fc(LmnAtom atom1, LmnAtom atom2);

LMN_EXTERN void lmn_hyperlink_print();


/* --------------------------------- */


typedef struct SameProcCxt {
  int atom;
  int arg;
  HyperLink start;
//  BOOL original;
  struct SameProcCxt *original;
} SameProcCxt;

typedef struct AtomProcCxt {
  void **spcs;
  unsigned int length;

  /* temporary */
//  void **atom_ary;
  Vector *hl_tree;
  int start_attr;

} AtomProcCxt;


//extern st_table *same_proccxt_map;
extern st_table *proccxt_map;

//extern SameProcCxt *same_proccxt;

LMN_EXTERN void lmn_proccxt_init_map();
LMN_EXTERN BOOL lmn_proccxt_has_map();
LMN_EXTERN BOOL lmn_proccxt_initialized(LmnInstrVar atom);
LMN_EXTERN void lmn_proccxt_clear();
LMN_EXTERN void lmn_proccxt_destroy();

LMN_EXTERN AtomProcCxt *lmn_proccxt_make_apc(int length);
//LMN_EXTERN void lmn_apc_init_atom_ary(AtomProcCxt *apc, int length);

#define UNUNIFIED 0
#define   UNIFIED 1

#define LMN_APC_SPC(A, I) ((A)->spcs[I])
#define LMN_APC_SPCS(A)   ((A)->spcs)
#define LMN_APC_LEN(A)    ((A)->length)
//#define LMN_APC_ATOM_ARY(A) ((A)->atom_ary)
#define LMN_APC_HL_TREE(A)  ((A)->hl_tree)
#define LMN_APC_SATTR(A)    ((A)->start_attr)

//LMN_EXTERN SameProcCxt *lmn_proccxt_make(int atom, int arg, BOOL original);
LMN_EXTERN SameProcCxt *lmn_proccxt_make(int atom, int arg, SameProcCxt *original);
LMN_EXTERN SameProcCxt *lmn_proccxt_make_dummy();
//LMN_EXTERN void lmn_proccxt_init(SameProcCxt *spc, int atom, int arg, BOOL original);
//LMN_EXTERN void lmn_proccxt_set(SameProcCxt *spc, int atom, int arg, BOOL original);
LMN_EXTERN void lmn_proccxt_set(SameProcCxt *spc, int atom, int arg, SameProcCxt *original);
LMN_EXTERN void lmn_proccxt_init_dummy(SameProcCxt *spc);
LMN_EXTERN BOOL lmn_proccxt_is_dummy(SameProcCxt *spc);

#define LMN_PROCCXT_ATOM(s)  ((s)->atom)
#define LMN_PROCCXT_ARG(s)   ((s)->arg)
#define LMN_PROCCXT_START(s) ((s)->start)
#define LMN_PROCCXT_ORI(s)   ((s)->original)


//LMN_EXTERN void lmn_proccxt_set_atom(SameProcCxt *spc, int n);
//LMN_EXTERN void lmn_proccxt_set_arg(SameProcCxt *spc, int n);
LMN_EXTERN int  lmn_proccxt_get_atom(SameProcCxt *spc);
LMN_EXTERN int  lmn_proccxt_get_arg(SameProcCxt *spc);



#endif /* LMN_HYPERLINK_H */
